unit dorm.adapter.Base;

interface

uses
  dorm.Commons,
  dorm.Filters,
  dorm.Mappings;

type
  TBaseAdapter = class abstract(TdormInterfacedObject)
  protected
    function GetWhereSQL(Criteria: TdormCriteria; FieldList: TMappingFieldList): string;
    function GetFieldMappingByAttribute(AttributeName: string;
      FieldList: TMappingFieldList): TMappingField;
    function EscapeString(const Value: string): string;
    function EscapeDate(const Value: TDate): string;
    function EscapeDateTime(const Value: TDate): string;
  public
    function GetSelectSQL(Criteria: TdormCriteria;
      AMappingTable: TMappingTable): string;
      virtual;
  end;

implementation

uses
  SysUtils;

{ TBaseAdapter }

function TBaseAdapter.GetFieldMappingByAttribute(AttributeName: string;
  FieldList: TMappingFieldList): TMappingField;
var
  fm: TMappingField;
begin
  for fm in FieldList do
    if fm.name = AttributeName then
      Exit(fm);
  raise EdormException.CreateFmt('Unknown field attribute %s', [AttributeName]);
end;

function TBaseAdapter.GetWhereSQL(Criteria: TdormCriteria; FieldList: TMappingFieldList): string;
var
  I: Integer;
  SQL: String;
  CritItem: TdormCriteriaItem;
  fm: TMappingField;
  d: TDate;
  dt: TDateTime;
begin
  for I := 0 to Criteria.Count - 1 do
  begin
    CritItem := Criteria.GetCriteria(I);
    if I > 0 then
      case CritItem.LogicRelation of
        lrAnd:
          SQL := SQL + ' AND ';
        lrOr:
          SQL := SQL + ' OR ';
      end;
    if CritItem is TdormCriteria then // recursive
    begin
      SQL := SQL + ' (' + GetWhereSQL(TdormCriteria(CritItem), FieldList) + ') ';
      Continue;
    end;

    fm := GetFieldMappingByAttribute(CritItem.Attribute, FieldList);
    SQL := SQL + fm.FieldName;
    case CritItem.CompareOperator of
      coEqual:
        SQL := SQL + ' = ';
      coGreaterThan:
        SQL := SQL + ' > ';
      coLowerThan:
        SQL := SQL + ' < ';
      coGreaterOrEqual:
        SQL := SQL + ' >= ';
      coLowerOrEqual:
        SQL := SQL + ' <= ';
      coNotEqual:
        SQL := SQL + ' != ';
      coLike:
        SQL := SQL + ' LIKE ';
    end;

    if fm.FieldType = 'string' then
      SQL := SQL + '''' + EscapeString(CritItem.Value.AsString) + ''''
    else if fm.FieldType = 'uniqueidentifier' then
      SQL := SQL + '''' + EscapeString(CritItem.Value.AsString) + ''''
    else if fm.FieldType = 'integer' then
      SQL := SQL + inttostr(CritItem.Value.AsInteger)
    else if fm.FieldType = 'boolean' then
      SQL := SQL + BoolToStr(CritItem.Value.AsBoolean)
    else if fm.FieldType = 'boolean' then
      SQL := SQL + BoolToStr(CritItem.Value.AsBoolean)
    else if fm.FieldType = 'date' then
    begin
      d := CritItem.Value.AsExtended;
      SQL := SQL + '''' + EscapeDate(d) + ''''
    end
    else if (fm.FieldType = 'timestamp') or (fm.FieldType = 'datetime') then
    begin
      dt := CritItem.Value.AsExtended;
      SQL := SQL + '''' + EscapeDateTime(dt) + ''''
    end
    else
      raise EdormException.CreateFmt('Unknown type %s in criteria', [fm.FieldType]);
  end;
  Result := SQL;
end;

function TBaseAdapter.GetSelectSQL(Criteria: TdormCriteria; AMappingTable: TMappingTable): string;
var
  SQL: string;
  _fields: TMappingFieldList;
  select_fields: string;
begin
  _fields := AMappingTable.Fields;
  select_fields := GetSelectFieldsList(_fields, true);
  if assigned(Criteria) and (Criteria.Count > 0) then
    SQL := 'SELECT ' + select_fields + ' FROM ' + AMappingTable.TableName + ' WHERE ' +
      GetWhereSQL(Criteria, _fields)
  else // Criteria is nil or is empty
    SQL := 'SELECT ' + select_fields + ' FROM ' + AMappingTable.TableName + ' ';
  Result := SQL;
end;

function TBaseAdapter.EscapeDate(const Value: TDate): string;
begin
  Result := FormatDateTime('YYYY-MM-DD', Value);
end;

function TBaseAdapter.EscapeDateTime(const Value: TDate): string;
begin
  Result := FormatDateTime('YYYY-MM-DD HH:NN:SS', Value);
end;

function TBaseAdapter.EscapeString(const Value: string): string;
begin
  Result := StringReplace(Value, '''', '''''', [rfReplaceAll]);
end;

end.
